/*  ----------------------------------------------------------------------------
 *  Arello Mobile
 *  Fast 3d Engine
 *  ----------------------------------------------------------------------------
 * Except where otherwise noted, this work is licensed under
 * http://creativecommons.org/licenses/by/3.0
 */

#include "PointBuffer.h"
#include "VertexBuffer.h"
#include "Hardware.h"
#include <logs/Log.h>
#include <Gluck.h>
#include "Creator.h"
#include <math/utl.h>

#define BUFFER_OFFSET(i) ((char *)NULL + (i))

namespace liba
{
namespace lib3d
{
namespace hard
{
namespace macx
{
				
PointBuffer::PointBuffer()
:	valid_flag( false ),
	point_buffer( 0 ),
	vertices_count( 0 ),
	stride( 0 ),
	point_sizes_stride( 0 ),
	colors_stride( 0 )
{
	glGenBuffers( 1, &point_buffer );
	if ( !point_buffer )
		THROW(Gluck(__FUNCTION__, "create_internal_point_buffer() failed"));
}

PointBuffer::~PointBuffer()
{
	valid_flag = false;

	VertexBuffer::reset_cached_buffer();
	glBindBuffer( GL_ARRAY_BUFFER, 0 );

	if( point_buffer )
		glDeleteBuffers( 1, &point_buffer );
	point_buffer = 0;
}

bool PointBuffer::is_valid()const
{
	return valid_flag;
}

bool PointBuffer::update(
			const float * positions, int positions_count,
			const float * diffuse_colors, int diffuse_colors_count,
			const float * sizes, int sizes_count )
{
	vertices_count = positions_count/3;

	if(positions_count == 0)
	{
		valid_flag = true;
		return false;
	}
	
	stride = 3 * sizeof(float);
	
	if(sizes_count)
	{
		point_sizes_stride = stride;
		stride += sizeof(float);
	}
	
	if(diffuse_colors_count)
	{
		colors_stride = stride;
		stride += 4 * sizeof(unsigned char);
	}
	
	VertexBuffer::reset_cached_buffer();
	glBindBuffer( GL_ARRAY_BUFFER, point_buffer );

	SimpleArray<float> vertices_data(positions_count + sizes_count + diffuse_colors_count);
	SimpleArray<float>::iterator data = vertices_data.begin();
	int delta_stride = stride/sizeof(float);
	for( int i = 0; i < vertices_count; ++i )
	{
		data[0] = *positions++;
		data[1] = *positions++;
		data[2] = *positions++;
		
		data += delta_stride;
	}
	
	if(point_sizes_stride)
	{
		data = vertices_data.begin();
		data += point_sizes_stride/sizeof(float);
		delta_stride = stride/sizeof(float);
		for( int i = 0; i < vertices_count; ++i )
		{
			*data = *sizes++;
			data += delta_stride;
		}
	}

	if(colors_stride)
	{
		data = vertices_data.begin();
		data += colors_stride/sizeof(float);
		delta_stride = stride/sizeof(float);
		for( int i = 0; i < vertices_count; ++i )
		{
			unsigned char * char_data = (unsigned char *)data;
			*char_data++ = math::clamp<float>( (*(diffuse_colors))*255, 0, 255 );
			*char_data++ = math::clamp<float>( (*(diffuse_colors+1))*255, 0, 255 );
			*char_data++ = math::clamp<float>( (*(diffuse_colors+2))*255, 0, 255 );
			diffuse_colors += 3;
			*char_data++ = math::clamp<float>( (*diffuse_colors++)*255, 0, 255 );
			
			data += delta_stride;
		}
	}

	glBufferData( GL_ARRAY_BUFFER, stride * vertices_count, vertices_data.begin(), GL_DYNAMIC_DRAW );
	
	valid_flag = true;
	return true;

}

void PointBuffer::render( int vertex_pos, int primitive_count )
{
	VertexBuffer::reset_cached_buffer();
	
	if( vertices_count == 0 /*|| !hardware->is_operational()*/ )
		return;
	
	glEnable(GL_POINT_SPRITE_OES);

	glBindBuffer( GL_ARRAY_BUFFER, point_buffer );
	glVertexPointer( 3, GL_FLOAT, stride, BUFFER_OFFSET(0) );
	
	glDisableClientState( GL_NORMAL_ARRAY );
	glDisable( GL_LIGHTING );
	
	glTexEnvi( GL_POINT_SPRITE_OES, GL_COORD_REPLACE_OES, GL_TRUE );
	
	if(point_sizes_stride)
	{
		glEnableClientState(GL_POINT_SIZE_ARRAY_OES);
		glPointSizePointerOES(GL_FLOAT, stride, BUFFER_OFFSET(point_sizes_stride));
	}
	else
		glDisableClientState(GL_POINT_SIZE_ARRAY_OES);
	
	if( colors_stride )
	{
		glColorPointer( 4, GL_UNSIGNED_BYTE, stride, BUFFER_OFFSET( colors_stride ) );
		glEnableClientState( GL_COLOR_ARRAY );
	}
	else
		glDisableClientState( GL_COLOR_ARRAY );
	
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	
	glDrawArrays(GL_POINTS, vertex_pos, primitive_count);
	
	//restore the states
	glDisable(GL_POINT_SPRITE_OES);
	glDisableClientState(GL_POINT_SIZE_ARRAY_OES);
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
}

} // namespace macx
} // namespace hard
} // namespace lib3d
} // namespace liba

